How to mock an Entity Framework DbContext and its DbSet properties
Entity Framework (EF) is a data access layer (DAL) that allows for easily accessing a database for your create, read, update, and delete (CRUD) actions. If you use Entity Framework, you don’t need to test your DAL CRUD actions. You just need to test your code that uses it.
With EF, you can mock your database, though it isn’t exactly easy. The database is interfaced through the DbContext. Tables are interfaces through a property of type DbSet
Ok, now that we have established that EF 6 DbContext mocking isn’t easy, let’s change that. Let’s make it easy.
Using the following two classes, you can easily mock your DbContext with a single line of code:
var mockDbContext = EntityFrameworkMockHelper.GetMockContext<MyDbContext>();
Note: While this single line of code successfully mocks your DbContext, it doesn’t add any default data. You still have to do that work yourself, but now it should be easy because you can use the very easy to use Add method on any table.
MockDbContext.Object.People.Add(new Person{ FirstName = "John", LastName = "Doe" });
Or if you have a List of sample data already created, you can add that list with AddRange.
MockDbContext.Object.People.AddRange(SamplePeople);
And here are the two classes I wrote to help with this. I must admit, I spent over three days researching this and getting this working. So hopefully, this saves you from having to do the same.
using Moq; using System.Collections.Generic; using System.Data.Entity; namespace LANDesk.Licensing.WebServices.Tests.Data { public class MockedDbContext<T> : Mock<T> where T : DbContext { public Dictionary<string, object> Tables { get { return _Tables ?? (_Tables = new Dictionary<string, object>()); } } private Dictionary<string, object> _Tables; } }
using System; using Moq; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; namespace LANDesk.Licensing.WebServices.Tests.Data { public static class EntityFrameworkMockHelper { /// <summary> /// Returns a mock of a DbContext /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static MockedDbContext<T> GetMockContext<T>() where T : DbContext { var instance = new MockedDbContext<T>(); instance.MockTables(); return instance; } /// <summary> /// Use this method to mock a table, which is a DbSet{T} oject, in Entity Framework. /// Leave the second list null if no adds or deletes are used. /// </summary> /// <typeparam name="T">The table data type</typeparam> /// <param name="table">A List{T} that is being use to replace a database table.</param> /// <returns></returns> public static DbSet<T> MockDbSet<T>(List<T> table) where T : class { var dbSet = new Mock<DbSet<T>>(); dbSet.As<IQueryable<T>>().Setup(q => q.Provider).Returns(() => table.AsQueryable().Provider); dbSet.As<IQueryable<T>>().Setup(q => q.Expression).Returns(() => table.AsQueryable().Expression); dbSet.As<IQueryable<T>>().Setup(q => q.ElementType).Returns(() => table.AsQueryable().ElementType); dbSet.As<IQueryable<T>>().Setup(q => q.GetEnumerator()).Returns(() => table.AsQueryable().GetEnumerator()); dbSet.Setup(set => set.Add(It.IsAny<T>())).Callback<T>(table.Add); dbSet.Setup(set => set.AddRange(It.IsAny<IEnumerable<T>>())).Callback<IEnumerable<T>>(table.AddRange); dbSet.Setup(set => set.Remove(It.IsAny<T>())).Callback<T>(t => table.Remove(t)); dbSet.Setup(set => set.RemoveRange(It.IsAny<IEnumerable<T>>())).Callback<IEnumerable<T>>(ts => { foreach (var t in ts) { table.Remove(t); } }); return dbSet.Object; } /// <summary> /// Mocks all the DbSet{T} properties that represent tables in a DbContext. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="mockedContext"></param> public static void MockTables<T>(this MockedDbContext<T> mockedContext) where T : DbContext { Type contextType = typeof(T); var dbSetProperties = contextType.GetProperties().Where(prop => (prop.PropertyType.IsGenericType) && prop.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)); foreach (var prop in dbSetProperties) { var dbSetGenericType = prop.PropertyType.GetGenericArguments()[0]; Type listType = typeof(List<>).MakeGenericType(dbSetGenericType); var listForFakeTable = Activator.CreateInstance(listType); var parameter = Expression.Parameter(contextType); var body = Expression.PropertyOrField(parameter, prop.Name); var lambdaExpression = Expression.Lambda<Func<T, object>>(body, parameter); var method = typeof(EntityFrameworkMockHelper).GetMethod("MockDbSet").MakeGenericMethod(dbSetGenericType); mockedContext.Setup(lambdaExpression).Returns(method.Invoke(null, new[] { listForFakeTable })); mockedContext.Tables.Add(prop.Name, listForFakeTable); } } } }
They increase renal excretion of potassium and hydrogen ions buy cialis and viagra online com 20 E2 AD 90 20Como 20Tomar 20Viagra 20Master 20 20Blague 20Viagra 20Et 20Alzheimer como tomar viagra master Meanwhile, the study also found that embryos that took longer to implant tended to lead to longer pregnancies, while pregnancies that displayed a late rise in progesterone were shorter by an average of almost two weeks compared to pregnancies with an early rise of progesterone
Hi,
have a question right now the mockDbcontext is inheriting from a DbContext, what if we already have a Samplebcontext created from Dbcontext and the Samplebcontext has parameterized constructor.
i have tried this implementation it fails at the following line " mockedContext.Setup(lambdaExpression).Returns(method.Invoke(null, new[] { listForFakeTable }));" as this particular implementation is done without a parameterized constructor and it fails if we have one.
Regards
jay
Yes. This started failing for me to once I moved to Moq 4.8. However, earlier versions of Moq work fine. I wonder what Moq changed? Right now I am just running with an earlier Moq version. Eventually, we need to figure out how to replace this line.
Hi Jay,
I have the same trouble. Do you figure out a solution?
Regards,
Shmatka
This line just doesn't work:
MockDbContext = EntityFrameworkMockHelper.GetMockContext();
Error 1: invalid token '=' in class, struct, or interface member declaration.
Error 2: The name "EntityFrameworkMockHelper.GetMockContext" does not exist in the current context.
Error 3: The name "MyDbContext" does not exist in the current context.
Error 4: Invalid token '(' in class, struct, or interface member declaration.
Is there another part to this article? How do you actually use it to test a controller?
dbSet.Setup(set => set.AddRange(It.IsAny<IEnumerable>())).Callback<IEnumerable>(table.AddRange);
dbSet does not contain a definition for "AddRange".
Same with "RemoveRange"
Sorry i am fresher.. where i have to use this code? in viewmodel class or testclass?
MockDbContext = EntityFrameworkMockHelper.GetMockContext();
MockDbContext.Object.People.Add(new Person{ FirstName = "John", LastName = "Doe" });
MockedDbContext.Object.People.AddRange(SamplePeople);
mockDbContext.Object.Members.Add(member);
threw exception:
System.NotSupportedException: Invalid setup on a non-virtual
Yes, Mock needs the property or method that you are mocking to be virtual.
How would you mock the where clause of the dbSet?
cause for me
is not working
Hi,
Is there a way to mock linq with Include() using this code?
I have not tried with Include().
//for .Include
dbSet.Setup(s => s.Include(It.IsAny())).Returns(dbSet.Object);
Hi, great help with these class, but i have a question, where "_Tables" is declared? i take your code but it is underlined red at "_Tables"
and where i can use MockDbSet? ^^
In a Unit Test. As stated in the article:
Using the following two classes, you can easily mock your DbContext with a single line of code:
Ah, you must not have used copy and paste. Take a closer look at the Tables property above. Notice that I put the backing field with the property, on the same line as the last } bracket. That way, when copy and pasting, you can copy the property and backing field in one copy and paste.
Great example. But in general developer needs to mock extension methods also: FirstOrDefault(), Single(), etc. The typical case is some service that checks whether exists concrete record and than make some logic. I believe that the best solution is real database mock.
You said: "I believe that the best solution is real database mock."
I disagree. You get 95% of your needs met with a code only mock and no actual database. The cost and time required to get that next 5% by doing a database mock, is massive. It is usually 10 times the work and has a huge maintenance burden.
Also, I may be wrong, but isn't FirstOrDefault there as part of linq? Because this mock retuns List, aren't FirstOrDefault available for List without having to mock them? So don't you get exactly what you are asking for without doing more mocking and without the massive undertaking of a database mock?
This is exactly what I needed; thank you!
I created a method using generics to get NBuilder to crank out data, but I was struggling with implementing generics within the dbset and context.
Thank you again, this is fantastic!
Hi,
Thanks a lot for your work. It´s really a help! I must just figure out how to add a mock for BeginTransaction.
Thanks. The code is really helpful. However, using this helper I am unable to use
. because I can't access to the mocked dbset. Can you help me on this? Sorry because i am not good with generic method and could not make it work.